// AST_XLab.cpp
// Author: Sudhir
// 
// AST header file.

#ifndef AST2_GUARD
#define AST2_GUARD
#include "SPA_Data_Types.h"


class expr {
	EXPRESSION_TYPE _expr_type;
	STRING _expr_string;
	
	expr *_lhs, *_rhs;
public:
	VAR_INDEX _expr_id;

	expr ( EXPRESSION_TYPE e );
	expr ( OP_TYPE );
	expr ( );
	EXPRESSION_TYPE getExpr_type();
	void setExprString( STRING s );
	STRING toString();
};
class numeric_expr : public expr {
	INDEX _id;
public: 
	numeric_expr ( VAR_INDEX variable_index );
	//numeric_expr (  );
	VAR_INDEX getVARIndex(){
		return this->_id;
	};
};
class composite_expr : public expr {
	OP_TYPE _op_type;
	expr *_rhs;
	expr *_lhs;
public:
	static const bool ADD_TO_LHS = true;
	static const bool ADD_TO_RHS = false;

	composite_expr( OP_TYPE operation_type );
	void setRHS ( expr *e );
	void setLHS ( expr *e );

	expr* getRHS ();
	expr* getLHS ();

};

// stmtNode
class stmtNode {
public:
	static enum LINK_TYPE { FOLLOWS, PARENT, NEXT_STMT, NEXT_TO_WHILE, 
		NEXT_TO_THEN, NEXT_TO_ELSE };
	static const int numLinks = 6;
	static const int INVALID_INDEX = -1;

	STATEMENT_INDEX _stmtIndex;
	STATEMENT_TYPE stmtType;

	PROC_INDEX _proc_id;
	stmtNode* links[numLinks];


	// Not used variables..
	stmtNode *_followsNode;
	stmtNode *_prevNode;
	stmtNode *_nextNode;
	//...

	void setLink( LINK_TYPE, stmtNode* );
	stmtNode( STATEMENT_TYPE st );
	INDEX getStmtIndex();

	STATEMENT_TYPE getStmtType();
};


class assignment_stmtNode : public stmtNode {
	expr *_lhs;
	expr *_rhs;
public:
	assignment_stmtNode( STATEMENT_INDEX stmt_id, VAR_INDEX LHS_var_id, PROC_INDEX curr_p_id);

	// get
	expr* getLHS ();
	expr* getRHS ();

	// set
	void setLHS ( expr *e );
	void setRHS ( expr *e );
};
class call_stmtNode : public stmtNode {
	PROC_INDEX _called_procedure_ID;
public:
	call_stmtNode( STATEMENT_INDEX id, PROC_INDEX called_proc_id, PROC_INDEX curr_p_id);
	VAR_INDEX getCallProcID ();
};
class if_stmtNode : public stmtNode {
	/*static const bool THEN_BLOCK = TRUE;
	static const bool ELSE_BLOCK = FALSE;*/
	
	VAR_INDEX _condition_variable;
public:
	stmtNode **addr_thenStmtListTail;
	static enum IF_BLOCK_TYPE { THEN_BLOCK, ELSE_BLOCK};
	if_stmtNode(  INDEX id, VAR_INDEX condition_variable, PROC_INDEX curr_p_id );
	VAR_INDEX getConditionVar ();
};
class while_stmtNode : public stmtNode {
	VAR_INDEX _condition_variable;
	stmtNode* _while_stmt_block_head;
public:
	while_stmtNode( INDEX id, VAR_INDEX condition_variable, PROC_INDEX curr_p_id);
	stmtNode** _getWhileStmtHead();
	VAR_INDEX getConditionVar ();
};

class procNode {
private:
	
public:
	PROC_INDEX _procedure_id;
	INTEGER _numStmts;


	stmtNode *_stmtList_Head;
	stmtNode *_stmtList_Tail;
	procNode ( PROC_INDEX proc_index){
		this->_procedure_id = proc_index;
		this->_stmtList_Head = this->_stmtList_Tail = NULL;
		this->_numStmts = 0;
	};
	PROC_INDEX getProcId () {
		return this->_procedure_id;
	};

	INDEX getFistStmtID () {
		return this->_stmtList_Head->getStmtIndex();
	};

	INDEX getLastStmtID () {
		return this->_stmtList_Tail->getStmtIndex();
	};

	// DO NOT USE THIS API...
	void addNewStmt ( stmtNode* newStmtNode ){
		if( this->_stmtList_Head == NULL ) {
			this->_stmtList_Head = newStmtNode;
			this->_stmtList_Tail = newStmtNode;
			cout <<"first node"<<endl;
		}else {
			if( this->_stmtList_Tail->stmtType == STATEMENT_Assign 
				|| this->_stmtList_Tail->stmtType == STATEMENT_Call ) {
					newStmtNode->_prevNode = this->_stmtList_Tail;
					this->_stmtList_Tail->_followsNode = newStmtNode;
					this->_stmtList_Tail->_nextNode = newStmtNode;
					this->_stmtList_Tail = newStmtNode;
			}else {
				newStmtNode->_prevNode = this->_stmtList_Tail;
				this->_stmtList_Tail->_nextNode = newStmtNode;
				this->_stmtList_Tail = newStmtNode;
			}
		}
	};
	// DO NOT USE THIS API...
	void addNewStmt2 ( stmtNode* newStmtNode ){
	};

	stmtNode* stmtList_Tail () {
		return this->_stmtList_Tail;
	};
	void setStmtListTail( stmtNode *s) {
		this->_stmtList_Tail = s;
	};
	stmtNode* stmtList_Head () {
		return this->_stmtList_Head;
	};
	void setStmtListHead( stmtNode *s) {
		this->_stmtList_Head = s;
	};
};


class AST {
private:
	vector<procNode*> _procedure_box;
	vector<stmtNode*> _stmts;
public:
	AST() {
	};

	void addStmt (stmtNode *newStmt);
	INTEGER numStmts ();
	stmtNode* StmtAt ( INDEX id );

	void addNewProcedure( procNode *p) {
		this->_procedure_box.push_back( p );
	};
	INTEGER numProcs() {
		return this->_procedure_box.size();
	};
	// DO NOT USE THIS API..
	procNode* getProcAt( INDEX proc_index ) {
		return (this->_procedure_box.at( proc_index ));
	};
	
};


#endif